#!/usr/bin/ksh
#
# zvmstat - print vmstat style info per Zone.
#           This uses DTrace (Solaris 10 3/05).
#
# This program must be run from the global zone as root.
#
# $Id: zvmstat 3 2007-08-01 10:50:08Z brendan $
#
# USAGE: 	zvmstat [-ht] [interval [count]]
#
#		zvmstat         # default output
#			-t      # print times
#  eg,
#		zvmstat 1       # print every 1 second
#		zvmstat 10 5    # print 5 x 10 second samples
#		zvmstat -t 5    # print every 5 seconds with time
#
#
# FIELDS:
#		re		page reclaims
#		mf		minor faults
#		fr		pages freed
#		sr		scan rate
#		epi		executable pages paged in
#		epo		executable pages paged out
#		epf		executable pages freed
#		api		anonymous pages paged in
#		apo		anonymous pages paged out
#		apf		anonymous pages freed
#		fpi		filesystem pages paged in
#		fpo		filesystem pages paged out
#		fpf		filesystem pages freed
#
# NOTES: 
# - Zone status should really be provided by Kstat, which currently
#   provides system wide values, per CPU and per processor set, but not per
#   zone. DTrace can fill this role in the meantime until Kstat supports zones.
# - First output does not contain summary since boot.
#
# SEE ALSO: prstat -Z
#
# COPYRIGHT: Copyright (c) 2005 Brendan Gregg.
#
# CDDL HEADER START
#
#  The contents of this file are subject to the terms of the
#  Common Development and Distribution License, Version 1.0 only
#  (the "License").  You may not use this file except in compliance
#  with the License.
#
#  You can obtain a copy of the license at Docs/cddl1.txt
#  or http://www.opensolaris.org/os/licensing.
#  See the License for the specific language governing permissions
#  and limitations under the License.
#
# CDDL HEADER END
#
# BUGS:
# - First output may not contain all zones due to how loops are achieved.
#   Check for newer versions.
#
# Author: Brendan Gregg  [Sydney, Australia]
#
# 11-May-2005   Brendan Gregg   Created this.
# 26-Jul-2005	   "      "	Improved code.
# 08-Jan-2006	   "      "	Last update.
#


##############################
# --- Process Arguments ---
#

### default variables
opt_time=0; interval=1; counts=1

### process options
while getopts ht name
do
	case $name in
	t)      opt_time=1 ;;
	h|?)    cat <<-END >&2
		USAGE: zvmstat [-ht] [interval [count]]
		       zvmstat         # default output
		               -t      # print times
		   eg,
		       zvmstat 1       # print every 1 second
		       zvmstat 10 5    # print 5 x 10 second samples
		       zvmstat -t 5    # print every 5 seconds with time
		END
		exit 1
	esac
done
shift $(( OPTIND - 1 ))

### option logic
if (( "0$1" > 0 )); then
        interval=$1; counts=-1; shift
fi
if (( "0$1" > 0 )); then
        counts=$1; shift
fi


#################################
# --- Main Program, DTrace ---
#
dtrace -n '
 #pragma D option quiet
 #pragma D option destructive
 #pragma D option switchrate=10

 /*
  * Command line arguments
  */
 inline int OPT_time   = '$opt_time';
 inline int INTERVAL   = '$interval';
 inline int COUNTER    = '$counts';

 /* 
  * Initialise variables
  */
 dtrace:::BEGIN 
 {
	secs = INTERVAL; 
	counts = COUNTER;
	zonemax = 0;
	listing = 1;
	re[""] = 0; pi[""] = 0; po[""] = 0;
	mf[""] = 0; sr[""] = 0; fr[""] = 0;
	epi[""] = 0; epo[""] = 0; epf[""] = 0;
	api[""] = 0; apo[""] = 0; apf[""] = 0;
	fpi[""] = 0; fpo[""] = 0; fpf[""] = 0;
 }

 /*
  * Build zonelist array
  *
  * Here we want the output of a command to be saved into an array
  * inside dtrace. This is done by running the command, sending the
  * output to /dev/null, and by probing its write syscalls from dtrace. 
  *
  * This is an example of a "scraper".
  */

 /*
  * List zones
  */
 dtrace:::BEGIN 
 {
	/* run zoneadm */
	system("/usr/sbin/zoneadm list > /dev/null; echo END > /dev/null");
 }

 /*
  * Scrape zone listing
  */
 syscall::write:entry
 /listing && (execname == "zoneadm") && 
 (curthread->t_procp->p_parent->p_ppid == $pid)/
 {
	/* read zoneadm output */
	zonelist[zonemax] = stringof(copyin(arg1, arg2 - 1));

	/* increment max number of zones */
	zonemax++;
 }

 /*
  * Finish scraping zones
  */
 syscall::write:entry
 /listing && (execname == "sh") && (ppid == $pid)/
 {
	/*
	 * this end tag lets us know our zonelist has finished.
	 * thanks A. Packer.
	 */
	listing = stringof(copyin(arg1, arg2 - 1)) == "END" ? 0 : 1;
 }

 /*
  * Record vminfo counters
  */
 vminfo:::pgrec      { re[zonename] += arg0; }
 vminfo:::as_fault   { mf[zonename] += arg0; }
 vminfo:::scan       { sr[zonename] += arg0; }
 vminfo:::execpgin   { epi[zonename] += arg0; }
 vminfo:::execpgout  { epo[zonename] += arg0; }
 vminfo:::execfree   { epf[zonename] += arg0; fr[zonename] += arg0; }
 vminfo:::anonpgin   { api[zonename] += arg0; }
 vminfo:::anonpgout  { apo[zonename] += arg0; }
 vminfo:::anonfree   { apf[zonename] += arg0; fr[zonename] += arg0; }
 vminfo:::fspgin     { fpi[zonename] += arg0; }
 vminfo:::fspgout    { fpo[zonename] += arg0; }
 vminfo:::fsfree     { fpf[zonename] += arg0; fr[zonename] += arg0; }

 /*
  * Timer
  */
 profile:::tick-1sec
 {
	secs--;
 }

 /*
  * Check for exit
  */
 profile:::tick-1sec
 /counts == 0/
 {
	exit(0);
 }

 /*
  * Print header line
  */
 profile:::tick-1sec
 /secs == 0/
 {
	/* set counters */
	secs = INTERVAL;
	counts--;
	zonei = 0;

	/* print time */
	OPT_time ? printf("\n%Y,\n",walltimestamp) : 1;

	/* print output line */
	printf("%10s %4s %5s %4s %5s %4s %4s %4s %4s %4s %4s %4s %4s %4s\n",
	    "ZONE", "re", "mf", "fr", "sr", "epi", "epo", "epf", "api", "apo",
	    "apf", "fpi", "fpo", "fpf");

	/* ensure zone writes are triggered */
	printf(" \b");
 }

 /*
  * Print zone status line
  *
  * This is a fairly interesting function in that it loops over the keys in 
  * an associative array and prints out the values. DTrace cant really do 
  * loops, and generally doesnt need to. We "cheat" by generating writes
  * in the above probe which in turn trigger the probe below which 
  * contains the contents of each loop. Dont do this at home! We are
  * supposed to use aggreagations instead, wherever possible.
  *
  * This is an example of a "feedback loop".
  */
 syscall::write:return
 /pid == $pid && zonei < zonemax/
 {
	/* fetch zonename */
	self->zone = zonelist[zonei];

	/* print output */
	printf("%10s %4d %5d %4d %5d %4d %4d %4d %4d %4d %4d %4d %4d %4d\n",
	    self->zone, re[self->zone], mf[self->zone], fr[self->zone],
	    sr[self->zone], epi[self->zone], epo[self->zone],
	    epf[self->zone], api[self->zone], apo[self->zone],
	    apf[self->zone], fpi[self->zone], fpo[self->zone],
	    fpf[self->zone]);
	
	/* clear values */
	re[self->zone] = 0; mf[self->zone] = 0; fr[self->zone] = 0;
	sr[self->zone] = 0; epi[self->zone] = 0; epo[self->zone] = 0;
	epf[self->zone] = 0; api[self->zone] = 0; apo[self->zone] = 0;
	apf[self->zone] = 0; fpi[self->zone] = 0; fpo[self->zone] = 0;
	fpf[self->zone] = 0;
	self->zone = 0;
	
	/* go to next zone */
	zonei++;
 }
'

